var md5 = require('./md5.min')
var ble = require('./ble')
let unit = require('./unit')
/*JavaScript 获取当前毫秒时间戳
var timestamp3 =new Date().getTime();
JavaScript 处理结构体
var buffer = new ArrayBuffer(2);
new DataView(buffer).setInt16(0, 256, true) // 设置值时使用小端字节序 
  // Int16Array 使用系统字节序，由此可以判断系统是否是小端字节序
DataView读写方法内存参考
set(偏移量以字节为单位,值,是否小端字节序默认为系统字节序)
setInt8：写入1个字节的8位整数。
setUint8：写入1个字节的8位无符号整数。
setInt16：写入2个字节的16位整数。
setUint16：写入2个字节的16位无符号整数。
setInt32：写入4个字节的32位整数。
setUint32：写入4个字节的32位无符号整数。
setFloat32：写入4个字节的32位浮点数。
setFloat64：写入8个字节的64位浮点数，

get(偏移量以字节为单位,是否小端字节序默认为系统字节序)
getInt8：读取1个字节，返回一个8位整数。
getUint8：读取1个字节，返回一个无符号的8位整数。
getInt16：读取2个字节，返回一个16位整数。
getUint16：读取2个字节，返回一个无符号的16位整数。
getInt32：读取4个字节，返回一个32位整数。
getUint32：读取4个字节，返回一个无符号的32位整数。
getFloat32：读取4个字节，返回一个32位浮点数。
getFloat64：读取8个字节，返回一个64位浮点数。
*/
//公用头 解析规则
var cmdHead = {
  type: [0, 1, 1],//命令类别 这里是0 uint8_t  
  index: [1, 1, 1],//命令序号 0 每次操作都从此命令开始 uint8_t  
};
//命令解析参数
const cmdPara = [
  //0 时间戳同步,明文不加密
  //var cmdTimestamp={
  {
    //head: cmdHead.type = 0,//命令头  
    timestamp: [2, 8, 1],//时间戳 uint64_t 
    ver: [10, 1, 1]//固件版本号 uint8_t  
  },
  //1 验证通过命令 都加密
  //var cmdPostOk={ //01 02 00 0a 00 00 00 00 00 00 00 12 d6 87 00 67 10 44 0c 00
  {
    //head: cmdHead,//.type=1,//命令头  
    count: [2, 2, 1],//选择数量 时长 分钟 uint16_t 
    mac: [4, 1, 6],//MAC地址 uint8_t [6]
    userId: [10, 4, 1],//用户ID uint32_t
    holdOn: [14, 4, 1],//货柜编号(设备ID) uint32_t 
    holdIndex: [18, 1, 1]//货舱号 uint8_t
  },

  //2 授权用户命令 
  //var cmdAuthUser={
  {
    //head: cmdHead,//.type = 2,//命令头  
    mac: [2, 1, 6],//MAC地址 加密 uint8_t [6]
    userId: [8, 4, 1],//用户ID uint32_t
    holdOn: [12, 4, 1]//货柜编号(设备ID) uint32_t 
  },
  //3 OTA命令
  //var cmdOta={
  {
    //head: cmdHead,//.type = 3,//命令头  
    mac: [2, 1, 6],//MAC地址 uint8_t [6]
    otaData: [8, 1, 12],//ota数据 uint8_t[12]  
    ver: [20, 1, 1]//最新固件版本号 uint8_t  
  },
  //4 回应ACK 明文
  //var cmdAck={
  {
    //head: cmdHead,//.type = 4,//命令头  
    ackStatu: [2, 1, 1],//Ack 状态 加密 0 成功 否则为错误码 设备不正常时的状态码也在这里 uint8_t 
    holdOn: [3, 4, 1],//货柜编号(设备ID) uint32_t 
    holdIndex: [7, 1, 1],//货舱号 uint8_t
    count: [8, 2, 1],//货舱剩余数量 服务剩余时间(分钟) uint16_t 
    ver: [10, 1, 1]//最新固件版本号 uint8_t  
  },
  //5 开货舱补仓门命令
  //var cmdOpenHold={
  {
    //head: cmdHead,//.type = 5,//命令头  
    mac: [2, 1, 6],//MAC地址 uint8_t [6]
    userId: [8, 4, 1],//用户ID uint32_t
    holdOn: [12, 4, 1]//货柜编号(设备ID) uint32_t 
  },
  //6 补货命令
  //var cmdSetHold={
  {
    //head: cmdHead,//.type = 6,//命令头  
    mac: [2, 1, 6],//MAC地址 uint8_t [6]
    holdOn: [8, 4, 1],//货柜编号(设备ID) uint32_t 
    holdIndex: [12, 1, 1],//货舱号 uint8_t
    count: [13, 2, 1]//货舱补充数量 加密 uint16_t
  },
  //7 绑定或变更货柜所有人命令
  //var cmdChengHold={
  {
    //head: cmdHead,//.type = 7,//命令头  
    mac: [2, 1, 6],//MAC地址 uint8_t [6]
    userId: [8, 4, 1],//用户ID uint32_t
    holdOn: [12, 4, 1]//货柜编号(设备ID) uint32_t 
  },
  //8 初始化货柜命令 设置货柜编号
  //var cmdSetHold={
  {
    //head: cmdHead,//.type = 8,//命令头  
    mac: [2, 1, 6],//MAC地址 uint8_t [6]
    userId: [8, 4, 1],//用户ID uint32_t
    holdOn: [12, 4, 1]//货柜编号(设备ID) uint32_t 
  }
]

let cmdObj = { type: 0, index: 0 }//解析后的命令对象
let cmdArray = new ArrayBuffer(20)
let objMac = new Uint8Array(new ArrayBuffer(6))
let actionType = 0; //操作类型 0 获取设备状态 1 同步时间 2 确认开始服务 3 暂停服务
const app = getApp()
//剔除重复命令
var stepOn = -1;//上一步步号
var stepType = -1;//上一步命令类别
var page
var timeInterval = 30000 //倒计时获取间隔 毫秒
let timeDelay


//加密解密
const encKey = 'ashdt334484364374h344v';//密钥
function cmdEncDec(dv) {
  let type = dv.getUint8(0)

  if (type == 0 || type == 4)//0 时间戳同步,4 不加密
    return

  /*
  //let cmd8bit=new Uint8Array(cmdBit)true
  for (let index = 2; index < dv.length; index++) {//2字节头不加密
    dv.setUint8(index,dv.getUint8(index)^encKey[index])
    //cmd[index]=cmd8bit[index]^encKey[index]
  }*/
}

//通信命令解析,true 成功
function cmdToObj(dv) {

  //console.log("cmdObj.type=",cmdObj.type);
  //console.log("cmdPara=",cmdPara[cmdObj.type]);
  for (let [key, value] of Object.entries(cmdPara[cmdObj.type])) {
    //console.log(key + ':' + value);
    if (value[1] == 1) {
      if (value[2] > 1) {//多字节获取
        cmdObj[key] = new Uint8Array(cmd, value[0], value[2]);
      } else {
        cmdObj[key] = dv.getUint8(value[0])
      }
    } else if (value[1] == 2) {
      cmdObj[key] = dv.getUint16(value[0], true)
    } else if (value[1] == 4) {
      cmdObj[key] = dv.getUint32(value[0], true)
    } else if (value[1] == 8) {
      cmdObj[key] = dv.getFloat64(value[0], true)
    } else {
      console.error("无效的属性配置");
    }
    //console.log(key + ':' + cmdObj[key]);
  }
  return true
}
//设置通信命令, true 成功 
function objToCmd() {
  let cmdType = cmdObj.type
  if (cmdType < 0 || cmdType >= cmdPara.length) {
    return false
  }
  let cmdTypePara = cmdPara[cmdType]
  //计算数组长度
  /*let maxLength=0;
  for (let [key, value] of Object.entries(cmdPara[cmdObj.type])) {
    if (value[0]+value[1]*value[2]>maxLength){
      maxLength=value[0]+value[1]*value[2]
    }
  }*/
  let dv = new DataView(cmdArray)
  dv.setUint8(0, cmdType)
  dv.setUint8(1, cmdObj.index)
  //console.log("命令参数 cmdTypePara=",cmdTypePara);
  for (let [key, value] of Object.entries(cmdTypePara)) {
    //console.log(key + ':' + value);
    if (value[1] == 1) {
      if (value[2] > 1) {//多字节设置
        for (let index = 0; index < cmdObj[key].length; index++) {
          dv.setUint8(value[0] + index, cmdObj[key][index])
        }
      } else {
        dv.setUint8(value[0], cmdObj[key])
      }
    } else if (value[1] == 2) {
      dv.setUint16(value[0], cmdObj[key], true)
    } else if (value[1] == 4) {
      dv.setUint32(value[0], cmdObj[key], true)
    } else if (value[1] == 8) {
      dv.setFloat64(value[0], cmdObj[key], true)
    } else {
      console.error("无效的属性配置",value);
    }
    //console.log(key + ':' + cmdObj[key]);
  }
  return true
}
//初始化网络硬件
function initNet(bleMac, bleDevId, curPage) {
  page = curPage
  ble.bleInit(bleMac, bleDevId, getBalanceTime)
}
//开始吸氧服务

//获取剩余时间,判断服务是否有未完成的服务,如果有,则继续倒计时
function getBalanceTime() {
  setActionType(0)//初始化判重标志
  page.disabledBt(2000)//禁止按钮
  ble.bleConnecte(function (recData) {
    console.log("获取剩余时间 收到ACK回应:", unit.ab2hex(recData));
    if (revAck(recData)) {//
      if (cmdObj.count > 0) {
        //设置剩余数据
        page.setData({
          balanceTime: unit.minutesToTime(cmdObj.count),//剩余时间
          isServer: true,
          stopTitle: "暂停服务",
          isStop: false,//是否暂停
        })
        //开始倒计时
        if (timeDelay) {
          clearTimeout(timeDelay)
          console.log("停止定时服务init:",  timeInterval);
        }
        timeDelay=setTimeout(getBalanceTime, timeInterval)
      } else {
        page.setData({
          balanceTime: '00:00',//剩余时间          
          time:'00:30',
          pr:'10.00',
          totalFee:1, //付款金额
          isServer: false,
          time:'00:30'
        })
      }
    }
    ble.decConnected()
  })
}

//开始吸氧服务, runCount=0,表示终止吸氧服务 isPause 表示暂停
function startServer(runCount,isPause) {
  setActionType(0)//初始化判重标志
  page.disabledBt(2000)//禁止按钮
  ble.bleConnecte(function (recData) {
    let countPause=0;
    console.log("开始吸氧服务 收到ACK回应:", unit.ab2hex(recData));
    if (revAck(recData)) {//
      countPause=cmdObj.count
      cmdObj.type = 1
      cmdObj.index = 2
      cmdObj.mac = objMac//mac: [4, 1, 6],//MAC地址 uint8_t [6]
      cmdObj.userId = 12345678//userId:[10,4,1],//用户ID uint32_t
      cmdObj.holdOn = 67543212//holdOn: [14, 4, 1],//货柜编号(设备ID) uint32_t 
      cmdObj.holdIndex = 12//holdIndex: [18, 1, 1]//货舱号 uint8_t
      if (isPause){
        cmdObj.count = 0// 设置剩余时间 0 表示终止服务
        page.data.tm=countPause
      } else{
        page.data.tm=runCount
        cmdObj.count = runCount// 设置剩余时间 0 表示终止服务
      }
      objToCmd()
      console.log('发送数据:', cmdObj);
      ble.writeValue(cmdArray, function () {
        ble.decConnected()
      })
      if (isPause){
        if (timeDelay) {
          clearTimeout(timeDelay)
          //console.log("停止定时服务:isPause",  timeInterval);
        }
        return
      } 
      if (runCount > 0) {
        //设置剩余数据
        page.setData({
          balanceTime: unit.minutesToTime(runCount),//剩余时间
          isServer: true
        })
        //开始倒计时
        if (timeDelay) {
          clearTimeout(timeDelay)
          //console.log("停止定时服务start:",  timeInterval);
        }
        timeDelay=setTimeout(getBalanceTime, timeInterval)
      } else { //转到付费页面
        page.setData({
          balanceTime: '00:00',//剩余时间
          isServer: false,
          tm:30,
          time:'00:30',
          pr:0.01,
          totalFee:1 //付款金额
        })
      }
    }
  })
}

//判断命令是否重复
function isRepeatCmd(dv) {
  cmdObj.type = dv.getUint8(0);
  cmdObj.index = dv.getUint8(1);
  if (cmdObj.type == stepType && cmdObj.index == stepOn) {
    console.error("命令重复:type= " + cmdObj.type + ",stepOn= " + cmdObj.index);
    return false
  }
  stepType = cmdObj.type
  stepOn = cmdObj.index
  return true
}

//解析不重命令,并不执行重复的返回数据 true
function revAck(cmd) {
  let dv = new DataView(cmd)
  if (!isRepeatCmd(dv)) {
    return false
  }
  cmdEncDec(dv)//解密
  if (!cmdToObj(dv)) {
    console.error('命令解析失败');
    return false;
  }
  console.log('接收数据:', cmdObj);
  return true
}
//通信命令解析处理 返回值 0 成功 -1 失败 1 成功并断开连接 -2 失败 并断开连接

//设置业务流程 0 获取设备状态 1 同步时间 2 确认开始服务 3 暂停服务
function setActionType(aType) {
  actionType = aType;
  stepOn = -1
  stepType = -1
}

module.exports = {
  setActionType: setActionType,
  startServer:startServer,//启停或暂停服务 runCount=0,表示终止吸氧服务 isPause 表示暂停
  getBalanceTime:getBalanceTime,//获取剩余时间,判断服务是否有未完成的服务,如果有,则继续倒计时
  initNet: initNet //初始化网,包括蓝牙和wifi
};
